# doctest: allows you to pick and choose methods to include from a module
# >> module M; def a; 'a'; end; def b; end; end
# >> class A; custom_include(M, :only => :a); end
# >> subject = A.new
# >> subject.a
# => "a"
# >> subject.b
# NoMethodError:
# doctest_require: class SimplegreyController; end; nil

class Class
  old_include = instance_method(:include)
  define_method(:custom_include) do |*args|
    default = {:alias => {}, :exclude => []}
    hash_arg = (Hash === args.last) ? default.update(args.pop) : default
    m = Module.new
    args.each{|mod| m.module_eval{ include mod } }
    #                            ^^^^^^^^^^^^^^^
    # check for "method shadowing" here and raise an exception or
    # something if you feel like it
    hash_arg[:alias].each_pair do |old, new|
      m.module_eval{ alias_method(new, old); undef_method(old) }
    end
    excluded = (Array === hash_arg[:exclude]) ? hash_arg[:exclude] : [hash_arg[:exclude]]
    # [*hash_arg[:exclude]] won't work on 1.9 cause there's no Object#to_a
    only = (Array === hash_arg[:only]) ? hash_arg[:only] : [hash_arg[:only]]
    raise 'cant exclude and only' + excluded.inspect + ' ' + only.inspect if excluded and only and excluded != [] and only != []
    if only
       all = []
       args.each {|mod|
	       all = mod.protected_instance_methods(false) + mod.private_instance_methods(false) + mod.instance_methods(false)
	   }
       excluded = all.map{|n| n.to_s} - only.map{|n| n.to_s} 
    end
    excluded.each{|meth| m.module_eval { undef_method(meth) } }
    old_include.bind(self).call(m)
  end
end
